package com.kx.websocket.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket 使得客户端和服务器之间的数据交换变得更加简单，允许服务端主动向客户端推送数据。
 * 在 WebSocket API 中，浏览器和服务器只需要完成一次握手，两者之间就直接可以创建持久性的连接，并进行双向数据传输。
 *
 * @ClassName WebsocketHandler
 * @Author Sun
 * @Date 2021/6/1 19:14
 * 按照面向对象思想,一定有一个类或者方法来帮我们处理某个事情
 * 我们当前类的主要作用是用于处理 websocket 的请求
 * 凭什么说这个类是处理 websocket 的类,所以需要继承一个类
 */
@Component
public class MyWebSocketHandler implements WebSocketHandler {

    //保存所有的会话
    private ConcurrentHashMap<String, WebSocketSession> allClients = new ConcurrentHashMap<>();

    private ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 当建立连接的时候执行
     *
     * @param session 当前建立的会话(连接)
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        //获取当前会话的保存的属性 map
        Map<String, Object> map = session.getAttributes();
        //拿到用户的唯一标识，在拦截器中保存的
        String name = (String) map.get("key");
        WebSocketSession webSocketSession = allClients.get(name);
        if (webSocketSession != null) {
            //说明原来已经建立过会话了，所以需要移除
            webSocketSession.close();
            webSocketSession=null;
        }
        //以连接的用户为 key 保存会话
        allClients.put(name, session);
    }

    /**
     * 当服务器收到文本消息的时候执行
     * 定义一下我们当前的需求
     * 我们用这个 websocket 的目的是为了聊天,客户和客服聊天,消息会先发给服务器,服务器再转发给接收者
     * 接收者我怎么知道是谁的? 知道了是谁之后,怎么找到对应的流(对应的封装对象)
     * 所以我们需要在某个地方保存所有的流(连接),并且还可以方便查找,你如我是发给张三的,我可以很方便的根据张三找到对应的流
     * 在这里我们主要是查找,因为要发送消息,说明必须已经保存了连接,那么应该在什么地方保存?什么地方出现了连接就在什么保存,当用户和服务器建立连接的时候就有连接了
     *
     * @param session 发送者的会话
     * @param message 当前发送的消息内容
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        //通过某种方式知道当前消息是发给谁的,比如我们要求你传递的内容必须是 json 字符串,其中有一个 to 表示发给谁
        //from表示谁发的,content 表示发送的内容
        String json = (String) message.getPayload();//获取到传递的内容
        Map map = objectMapper.readValue(json, Map.class);

        String to = (String) map.get("to");//发送给谁
        String from = (String) map.get("from");//谁发送的
        String content = (String) map.get("content");//发送的内容


        WebSocketSession socketSession = allClients.get(to);//以目标作为 key 找到目标的会话
        //会话必须是存在的同时连接必须是打开的没有关闭
        if (socketSession != null && socketSession.isOpen()) {
            socketSession.sendMessage(new TextMessage("收到来自于:" + from + "的消息, 内容是:" + content));//发送消息
            return;
        }
        //代表目标不在线
        //保存消息,等对方上线后发送
        session.sendMessage(new TextMessage("对方不在线"));
    }

    /**
     * 当发生错误的时候执行，用来处理错误信息
     *
     * @param session
     * @param exception
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if (session != null) {
            allClients.remove(session.getAttributes().get("key"));//发生错误的时候删除会话
            session.close();
        }
    }

    /**
     * 当连接关闭的时候执行
     *
     * @param session
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        if (session != null) {
            allClients.remove(session.getAttributes().get("key"));//发生错误的时候删除会话
        }
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    @EventListener
    public void onEvent(Map map) throws IOException {
        String to = (String) map.get("to");//发送给谁
        String from = (String) map.get("from");//谁发送的
        String content = (String) map.get("content");//发送的内
        //以目标作为key，找到目标会话
        WebSocketSession socketSession = allClients.get(to);
        //会话必须是存在的同时连接必须是打开的没有关闭
        if (socketSession != null && socketSession.isOpen()) {
            socketSession.sendMessage(new TextMessage("收到来自于:" + from + "的消息, 内容是:" + content));//发送消息
            return;
        }
        //代表目标不在线
        //保存消息,等对方上线后发送
        socketSession.sendMessage(new TextMessage("对方不在线"));
    }

}
